﻿using System;
using System.Collections.Generic;
using System.IO;
using CoreVirtuoso;
using CoreVirtuoso.Config;
using CoreVirtuoso.Helpers;
using VDS.RDF;
using VDS.RDF.Query;
using VDS.RDF.Storage;
using VirtuosoDal.Config;
using VirtuosoDal.Entities;
using VirtuosoDal.Helpers;
using VirtuosoDal.Repositories.RdfRepositories.Interfaces.Events;

namespace VirtuosoDal.Repositories.RdfRepositories.Events
{
   public class EventsContentRepository : IEventsContentRepository
   {
      public ResultSet<Event> GetEventsForSearch(string text, string geoObjectUri, string language, int page = 1, int pageSize = -1)
      {
         int eventsForSearchCount = GetEventsForSearchCount(text, geoObjectUri);

         ResultSet<Event> resultSet =
            new ResultSet<Event>(eventsForSearchCount);
         if (eventsForSearchCount == 0)
            return resultSet;

         if (pageSize == -1)
            pageSize = PagingConfig.PageSize;
         string offsetAndLimitCriteria = SparqlOffsetAndLimitClauseHelper.GetOffsetAndLimitClause(page, pageSize);

         bool inferenceIsEnabled;
         string query = EventContentRepositoryHelper.BuildQuery(QueryType.Query,
                                                                text,
                                                                geoObjectUri,
                                                                offsetAndLimitCriteria,
                                                                out inferenceIsEnabled);


         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager, query,
                                                                inferenceIsEnabled ? RuleSets.EventsRuleSet : null);

            resultSet.Values = GetEvents(SparqlResultSetConverter.ToListOfUris(sparqlResultSet), language);
         }

         return resultSet;
      }

      public int GetEventsForSearchCount(string text, string geoObjectUri)
      {
         bool inferenceIsEnabled;
         string query = EventContentRepositoryHelper.BuildQuery(QueryType.Count,
                                                                text,
                                                                geoObjectUri,
                                                                null,
                                                                out inferenceIsEnabled);

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            object result = QueryHelper.ExecuteScalar(virtuosoManager, query,
                                                      inferenceIsEnabled ? RuleSets.EventsRuleSet : null, true);
            return Convert.ToInt32(result);
         }
      }

      public ResultSet<Event> GetUserEvents(string userName, string language, int page = -1, int pageSize = 10)
      {
         int userEventsCount = GetUserEventsCount(userName);

         var resultSet = new ResultSet<Event>(userEventsCount);
         if (userEventsCount == 0)
            return resultSet;

         if (page == -1)
            page = PagingConfig.PageSize;

         string offsetAndLimit = SparqlOffsetAndLimitClauseHelper.GetOffsetAndLimitClause(page, pageSize);

         string query = String.Format("prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
                                      "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#> " +
                                      "prefix owl:       <http://www.w3.org/2002/07/owl#> " +
                                      "prefix  rdfs:     <http://www.w3.org/2000/01/rdf-schema#> " +
                                      "prefix events:    <http://beaware.at/ontology#> " +
                                      "SELECT DISTINCT ?event " +
                                      "FROM @graph " +
                                      "WHERE " +
                                      "{{ " +
                                      " ?event ?property ?value . " +
                                      " ?event events:lastEditingDateTime ?lastEditing . " +
                                      " ?event events:createdBy @userName . " +
                                      "}} " +
                                      "ORDER BY DESC (?lastEditing)" +
                                      "{0} ", offsetAndLimit);

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.EventsContentNamespace));
         sparqlParameterizedString.SetLiteral("@userName", userName);

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager,
                                                                sparqlParameterizedString.ToString(),
                                                                null);

            resultSet.Values = this.GetEvents(SparqlResultSetConverter.ToListOfUris(sparqlResultSet), language);

            return resultSet;
         }
      }

      public int GetUserEventsCount(string userName)
      {
         string query = "prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
                        "prefix owl:       <http://www.w3.org/2002/07/owl#> " +
                        "prefix  rdfs:     <http://www.w3.org/2000/01/rdf-schema#> " +
                        "prefix events:    <http://beaware.at/ontology#> " +
                        "SELECT count(DISTINCT ?event) " +
                        "FROM @graph " +
                        "WHERE " +
                        "{ " +
                        " ?event ?property ?value . " +
                        " ?event events:lastEditingDateTime ?lastEditing . " +
                        " ?event events:createdBy @userName . " +
                        "} ";

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.EventsContentNamespace));
         sparqlParameterizedString.SetLiteral("@userName", userName);

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            object result = QueryHelper.ExecuteScalar(virtuosoManager, sparqlParameterizedString.ToString(),
                                                      RuleSets.EventsRuleSet, true);
            return Convert.ToInt32(result);
         }
      }

      private List<Event> GetEvents(List<string> uris, string language)
      {
         if (uris == null || uris.Count == 0)
            return new List<Event>();

         string urisEnumerationString = UriEnumerationHelper.GetUriEnumerationString(uris.Count);

         string query = String.Format("prefix events:    <http://beaware.at/ontology#> " +
                                      "prefix geonames:      <http://www.geonames.org/ontology#> " +
                                      "SELECT DISTINCT ?event ?property ?value " +
                                      "FROM @graph " +
                                      "WHERE " +
                                      "{{ " +
                                      " ?event ?property ?value . " +
                                      " FILTER " +
                                      " ( " +
                                      "  ?event in ({0}) " +
                                      " ) " +
                                      "}}", urisEnumerationString);

         var sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.EventsContentNamespace));
         sparqlParameterizedString.SetUri("@geoNamesGraph", new Uri(Namespaces.GeoNamesOntologyNamespace));
         UriEnumerationHelper.FillUriEnumerationStringParameters(sparqlParameterizedString, uris);

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager, sparqlParameterizedString.ToString(), null);

            var events = SparqlResultSetConverter.FromTriples(sparqlResultSet, uris, language);

            return events;
         }
      }

      public Event GetEvent(string eventUri, string language)
      {
         string query = "prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> " +
                        "prefix owl:    <http://www.w3.org/2002/07/owl#> " +
                        "prefix  rdfs:     <http://www.w3.org/2000/01/rdf-schema#> " +
                        "prefix events:    <http://www.semanticweb.org/ontologies/2010/1/Events.owl#> " +
                        "SELECT DISTINCT ?subject ?predicate ?value " +
                        "FROM @graph " +
                        "WHERE " +
                        "{ " +
                        " ?subject ?predicate ?value. " +
                        " FILTER (?subject = @event) " +
                        "} ";

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.EventsContentNamespace));
         sparqlParameterizedString.SetUri("@event", new Uri(eventUri));

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager,
                                                                sparqlParameterizedString.ToString(),
                                                                null);

            List<Event> events = SparqlResultSetConverter.FromTriples(sparqlResultSet, new List<string>() { eventUri }, language);
            if (events.Count != 1)
               throw new InvalidDataException("For one uri SparqlResultSetConverter.FromTriples should return one Event instance");

            return events[0];
         }
      }

      public void RemoveEventMetaData(VirtuosoManager virtuosoManager, string eventUri)
      {
         string query = "sparql " +
                        "prefix events: @eventsPrefix " +
                        "DELETE FROM @graph" +
                        "{ " +
                        "  @event ?p ?o " +
                        "} " +
                        "WHERE " +
                        "{ " +
                        "   @event ?p ?o . " +
                        "   FILTER ( ?p != events:creationDateTime && ?p != events:createdBy) " +
                        "}";

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@eventsPrefix", new Uri(Namespaces.EventsOntologyNamespace));
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.EventsContentNamespace));
         sparqlParameterizedString.SetUri("@event", new Uri(eventUri));

         virtuosoManager.ExecuteNonQuery(sparqlParameterizedString.ToString());
      }

      public string GetEventCreator(string eventUri)
      {
         string query = "prefix events: <http://beaware.at/ontology#> " +
                        "SELECT ?userName " +
                        "FROM @graph " +
                        "WHERE " +
                        "{" +
                        " @event events:createdBy ?userName . " +
                        "}";

         SparqlParameterizedString sparqlParameterizedString = new SparqlParameterizedString(query);
         sparqlParameterizedString.SetUri("@graph", new Uri(Namespaces.EventsContentNamespace));
         sparqlParameterizedString.SetUri("@event", new Uri(eventUri));

         using (var virtuosoManager = new VirtuosoManager(ConnectionStringsHelper.GetVirtuosoConnectionString()))
         {
            SparqlResultSet sparqlResultSet = QueryHelper.Query(virtuosoManager, sparqlParameterizedString.ToString(),
                                                                null);
            if (sparqlResultSet.Count == 0)
               return null; //no event

            return sparqlResultSet.Results[0][0].ToString();
         }
      }
   }
}